softwarefandomcom_de-20200213-history
Fpii
Fpii ist eine funktionale und objektorientierte Programmiersprache, die auf den von John Backus vorgeschlagenen FP-Systemen aufbaut. Die Objektorientierung ist auf die Polymorphie der Methoden in einem Klassenobjekt samt Vererbung reduziert. Da es in Fpii keine Variablen gibt, werden als Ersatz so genannte "Dictionary-Objekte" verwendet, die wie lokale Variablen eingesetzt werden können. Dadurch bleibt die referentielle Transparenz gewahrt. Die Kombinatoren und anderen Operatoren werden in der Infix-Schreibweise in den Sequenzen verwendet. Das Infix-Schema wurde auch auf die Objekte übertragen, um nur ein Klammernpaar, das der Listendarstellung, einzusetzen. Die Quelltexte werden mittels eines JIT-Compilers in eine interne Listendarstellung umgewandelt, es können auch Listen von Listen auftreten, wie das bei der Programmiersprache LISP der Fall ist. Diese interne Darstellung wird dann von einem Interpreter verarbeitet. Seiteneffekte werden durch die Reaktion der Ausgabe auf fios-Objekte ausgelöst. __TOC__ In der Regel ist die Verarbeitungsrichtung rechts-vor-links wie bei APL. Eine Ausnahme ist zum Beispiel die if-else-Struktur: p1 → f1;p2 → f2;etc handvoll Datentypen * (1 + 2) ist ein Beispiel für eine Sequenz, cons-Datentyp. * (klasse1 :: element1 element2 element3 ...) ist ein Objekt, diese Gruppe gehört zu den Sequenzen. * 12 ist ein Beispiel für float, atomarer Datentyp. * name1 ist ein ident, atomarer Datentyp. * 'A' ist ein Beispiel für char, ein atomarer Datentyp. * ( ) ist der null-Datentyp. Die Datentypen haben eine aktive (als Funktionen) und passive Eigenschaften. Anwendungsbeispiele 1:(list :: aa bb cc) → aa 2:(list :: aa bb cc) → bb pop:(list :: aa bb cc) → (list :: bb cc) rev:(string :: 'hallo') → (string :: 'ollah') ((1='0'&)→*pop):(string :: '007') → (string :: '7') "löscht die führenden Nullen mit einer while-Schleife (→*)" (ucase a id):(string :: 'hallo') → (string :: 'HALLO') "a ist der Mapping-Operator, der ucase auf die string-Elemente anwendet" (inc o 2):(list :: 33 44 55) → 45 "(o) verkettet die Funktion 2 mit dem darauffolgendem Increment" (4,2,1,3,nil):(list :: ein ist satz dies) → (list :: dies ist ein satz) ((bb#),(cc#),(aa#),nil):(dict :: ( ) aa cool bb barney cc ist) → (list :: barney ist cool) Erzeugen und ausführen von Funktionalen, "cut" schneidet das "list ::" vorne weg, "on" wendet die erzeugte Funktion an: ((cut°2,1,3,nil) on 4):(list :: + 1 2 (list :: 22 33)) → 55 Quicksort-Algorithmus: qsort:=nilp→id; ((qsort°3)++1,qsort°4) °((not°nilp°2)→*1,(pop°2),(1>1°2)→(((1°2),3),4,nil);3,((1°2),4),nil) °1,pop,(nil as _1),(nil as _1),nil dict-Objekte als Namensräume Ein dict-Objekt hat den Aufbau: (dict :: Super-dict ident1 value1 ident2 value2 ident3 value3 ... ... )' Mit den Operatoren ''get und put wird in dem dict-Objekt nach dem entsprechenden Bezeichner gesucht und der zugeordnete Wert hervorgeholt oder gesetzt (dabei wird das dict-Objekt zum Teil neu generiert). Length mit Instanzenvariablen, Beispiel wie der Einsatz von lokalen Variablen mit dict-Objekten simuliert wird: vlength:=(y#)°((not°nilp°x#)→*(y#=inc°y#)°(x#=pop°x#))°(y#=0&)°(id←x)°id,nil fios-Objekte regeln die Seiteneffekte Ein fios-Objekt ist abgeleitet vom dict-Objekt und hat den Aufbau: '''(fios :: Super-dict self Objekt op Aktion arg Argument param Durchleitungsdaten bind Folgefunktion ) Funktionen müssen frei von Nebeneffekten sein (referentielle Transparenz). Das fios-Objekt kann also nur innerhalb einer Funktion generiert werden und die Funktion wird mit dem Resultat des fios-Objekts beendet. Bis das fios-Objekt zur Toplevel-Ausgabe gelangt, doch zuvor wird es dort abgefangen und dem FIOSystem zur Bewertung übergeben. Aus den Instanzenwerten wird dann der entsprechende Nebeneffekt ermittelt. Die Auslösung des Nebeneffektes, das kann z.B. das Lesen einer Datei sein, führt zu einem list-Objekt, das das Resultat (hier: Inhalt der Datei) und die Durchleitungsdaten enthält. Dieses list-Objekt ist das Argument der Folgefunktion, mit der der Programmverlauf fortgesetzt wird. Objektorientierung Definitionsschema für Klassenbezeichner mit Klassenobjekt: klasse1:=class :: superklasse1 selektor1 methode1 selektor2 methode2 ... ... Definitionschema für Operatoren und Definition der Methoden: operator1:=selektor1 eeop funktion_für_atomare_datentypen1 methode1:=((1°self)*(1°arg))+((2°self)*(2°arg)) objektorientierte Anwendung: (klasse1 :: 12 15) operator1 (list :: 3 2) → 66 Operator-Argument Das oparg-Objekt wird angelegt, wenn ein Operator in einem Tupel ausgeführt wird. "Hier mit der Funktion id als Operator:" (3 id 2):(string :: 'A' 'B' 'C') → (oparg :: (3 id 2) (string :: 'ABC')) "mit self und arg kann man nun auf den Funktionsterm und das Argument zugreifen." Verwendet man einen eeop-Term statt id, so wird der linke und der rechte Teil auf das Argument angewendet, und daraus eine Liste erzeugt. (3 (nop eeop id) 2):(string :: 'A' 'B' 'C') → (list :: 'CB') "mit self und arg kann auch hier auf das linke und rechte Resultat in der Liste zugegriffen werden." nop verhindert, dass in der Klasse des linken Resultats nach dem Selektor gesucht wird. variablenfrei versus lambda Variablen haben den Vorteil, dass dem zugeordneten Wert ein kurze Beschreibung gegeben werden kann, was der Dokumentation der Programme zugute kommt. Möchte man Variablen verwenden, wie beim Lambda-Kalkül, muss man sich für eine Bindungsstrategie entscheiden, der lexikalischen Bindung oder der dynamischen Bindung. Variablenfrei Programmieren bedeutet stattdessen Zahlen für die Positionen der Parameter zu gebrauchen bzw. "Instanzenvariablen", wenn man mit dict-Objekten arbeitet. Die Verwendung der dynamischen Bindung bringt sogar ein ernstzunehmendes Problem mit sich, dass nun in LISP dokumentiert werden soll, da es den Lambda-Kalkül unterstützt. Problem bei der dynamischen Bindung ; es wird ein LISP mit dynamischer Bindung verwendet (set 'pi 3.141592) (defun kreisflaeche ® (* pi (* r r))) ; noch läuft alles ok (kreisflaeche 10) → 314.1592 ; es wird eine Erweiterung durchgeführt (defun kreisflaechemitdurchmesser (pi d) (kreisflaeche (/ d 2))) ; nun passiert etwas unerwartetes (kreisflaechemitdurchmesser 3 20) → 300 ; Es kommt ein falsches Ergebnis zustande, was auf die dynamische Bindung zurückzuführen ist. ; Dies ist nur ein harmloses Beispiel, tatsächlich entstehen so schwer zu entdeckende Fehler. Evaluation zu FP-Systemen und Fpii Warum John Backus in seinem Aufsatz zu den FP-Systemen auf Variablen verzichtete, ist zum Beispiel weil er den Einsatz des Lambda-Kalküls mit seinen zugrunde liegenden Regeln vermeiden wollte. Vielleicht kannte er das Problem bei der dynamischen Bindung von Variablen. Vielleicht war es auch die Komposition aus der Funktionalanalysis und die anderen funktionalen Formen, die einen eleganten Programmaufbau ohne Variablen gestatteten (oder forderten); mit der Schnelligkeit von APL-Operatoren. Eine Programmiersprache in der Referenzelle Transparenz garantiert wird kann nur den Gebrauch von lokalen Variablen realisieren, die für die Verwendung von zum Beispiel einer While-Schleife unerlässlich sind, wenn dict-Objekte zum Einsatz kommen. Mit dem Lambda-Kalkül können Schleifen, ist Referenzielle Transparenz ein Kriterium, nur endrekursiv definiert werden, was eine Einschränkung ist auch weil Konstrukte wie While-Schleifen in diesem Fall nicht möglich sind. Weblinks * Can Programming Be Liberated from the von Neumann Style? A Functional Style and Its Algebra of Programs Stanford University, 1978 (PDF-Datei; 2,87 MB) * Kritik am Backus-Aufsatz von Prof. Dr. E. W. Dijkstra (PDF-Datei; 143 kB) * FP-Solo-2009 Download * FPr-2010 Download Kategorie:Funktionale Programmiersprache Kategorie:Objektorientierte Programmiersprache